<script lang="ts">
	import {
		CHAT_ROLE_TYPE,
		LIST_MESSAGE_TYPE,
		SendMessageResponse_CODE,
		type ChatMessage
	} from '$lib/api/proto/roadmap_rag_api';
	import { appStore } from '$lib/stores/app.svelte';
	import { userStore } from '$lib/stores/user.svelte';
	import { callApi } from '$lib/utils/call_utils';
	import { BotMessageSquare, UserRound } from 'lucide-svelte';
	import { roadmapStore } from '../store.svelte';
	import moment from 'moment';
	import SvelteMarkdown from 'svelte-markdown';

	let curRagConfigId = $state(
		roadmapStore.ragConfigList.length > 0 ? roadmapStore.ragConfigList[0].ragConfigId : ''
	);

	let msgList = $state([] as ChatMessage[]);
	let scrollMessageId = $state('');

	let newMsgContent = $state('');
	let inAnswer = $state(false);

	async function loadMsgList() {
		if (userStore.userSession == null || userStore.userInfo == null) {
			return;
		}
		const res = await callApi(
			appStore.roadmapRagApiClient.listMessage({
				sessionId: userStore.userSession,
				roadmapId: roadmapStore.roadmapId,
				targetMemberUserId: userStore.userInfo.userId,
				ragConfigId: curRagConfigId,
				listType: LIST_MESSAGE_TYPE.LIST_MESSAGE_BEFORE,
				refMessageId: '',
				limit: 20
			}).response
		);
		msgList = res.messageList;
		if (msgList.length > 0) {
			scrollMessageId = msgList[msgList.length - 1].messageId;
		}
	}

	async function loadBefore() {
		if (userStore.userSession == null || userStore.userInfo == null || msgList.length == 0) {
			return;
		}
		const res = await callApi(
			appStore.roadmapRagApiClient.listMessage({
				sessionId: userStore.userSession,
				roadmapId: roadmapStore.roadmapId,
				targetMemberUserId: userStore.userInfo.userId,
				ragConfigId: curRagConfigId,
				listType: LIST_MESSAGE_TYPE.LIST_MESSAGE_BEFORE,
				refMessageId: msgList[0].messageId,
				limit: 20
			}).response
		);
		const tmpList = [...res.messageList, ...msgList];
		msgList = tmpList;
	}

	async function onAnswer(userMessageId: string, answerMsg: ChatMessage) {
		let index = msgList.findIndex((item) => item.messageId == userMessageId);
		if (index == -1) {
			await loadMsgList();
		}
		const tmpList = msgList.slice();
		index = tmpList.findIndex((item) => item.messageId == answerMsg.messageId);
		if (index == -1) {
			tmpList.push(answerMsg);
		} else {
			tmpList[index] = answerMsg;
		}
		msgList = tmpList;
	}

	async function sendMessage() {
		if (userStore.userSession == null || newMsgContent.trim() == '') {
			return;
		}
		const resp = appStore.roadmapRagApiClient.sendMessage({
			sessionId: userStore.userSession,
			roadmapId: roadmapStore.roadmapId,
			ragConfigId: curRagConfigId,
			content: newMsgContent
		});

		newMsgContent = '';

		try {
			inAnswer = true;
			let responseContent = '';
			const unlistenFn = resp.responses.onNext((msg, error) => {
				if (msg != undefined) {
					if (msg.code != SendMessageResponse_CODE.CODE_OK) {
						appStore.addToastMessage({
							level: 'error',
							content: msg.errMsg,
							keepTime: 2000
						});
					} else {
						responseContent += msg.assistantContentPart;
						scrollMessageId = msg.assistantMessageId;
						onAnswer(msg.userMessageId, {
							messageId: msg.assistantMessageId,
							chatRole: CHAT_ROLE_TYPE.CHAT_ROLE_ASSISTANT,
							content: responseContent,
							timeStamp: msg.assistantTimeStamp,
							ownerUserId: userStore.userInfo?.userId ?? '',
							ownerDisplayName: '',
							ownerLogoUri: ''
						});
					}
				}
				if (error != undefined) {
					appStore.addToastMessage({
						level: 'error',
						content: error.message,
						keepTime: 2000
					});
				}
			});

			await resp;
			unlistenFn();
		} finally {
			inAnswer = false;
		}
	}

	function msgAction(node: HTMLDivElement) {
		const nodeMsgId = node.getAttribute('data-msg-id');

		$effect(() => {
			if (scrollMessageId == nodeMsgId) {
				node.scrollIntoView();
				scrollMessageId = '';
			}
		});
	}

	$effect(() => {
		loadMsgList();
	});
</script>

<div class="bg-base-200 w-[400px] rounded-box">
	<div class="flex justify-between p-2 items-center">
		<h2 class="font-bold text-2xl">学习助理</h2>
		<select class="select w-[150px]" bind:value={curRagConfigId}>
			{#each roadmapStore.ragConfigList as ragConfig}
				<option value={ragConfig.ragConfigId}>{ragConfig.basicConfig?.name}</option>
			{/each}
		</select>
	</div>
	<div
		class="h-[calc(100vh-500px)] overflow-y-auto m-2"
		onscroll={(e) => {
			if (
				e.currentTarget.scrollHeight > e.currentTarget.clientHeight &&
				e.currentTarget.scrollTop < 20
			) {
				loadBefore();
			}
		}}
	>
		{#each msgList as msg}
			<div class="flex mt-2 p-1 justify-between">
				<div class="flex gap-2">
					{#if msg.chatRole == CHAT_ROLE_TYPE.CHAT_ROLE_USER}
						{#if msg.ownerLogoUri == ''}
							<UserRound size={20} />
						{:else}
							<div class="avatar">
								<div class="w-[48px] rounded-full">
									<img alt="avatar" src={msg.ownerLogoUri} />
								</div>
							</div>
						{/if}
						{msg.ownerDisplayName}
					{:else if msg.chatRole == CHAT_ROLE_TYPE.CHAT_ROLE_ASSISTANT}
						<BotMessageSquare />AI助理
					{/if}
				</div>
				<div>{moment(Number(msg.timeStamp)).format('YYYY-MM-DD HH:mm:ss')}</div>
			</div>
			<div
				class="bg-base-100 m-2 p-2 rounded-box text-wrap break-all overflow-hidden"
				data-msg-id={msg.messageId}
				use:msgAction
			>
				{#if msg.chatRole == CHAT_ROLE_TYPE.CHAT_ROLE_USER}
					{msg.content}
				{:else if msg.chatRole == CHAT_ROLE_TYPE.CHAT_ROLE_ASSISTANT}
					<SvelteMarkdown source={msg.content} />
				{/if}
			</div>
		{/each}
	</div>
	<div class="p-2 relative">
		<textarea
			class="textarea pr-[100px] w-full"
			placeholder={inAnswer ? '思考并回答中...' : '请输入...'}
			rows="3"
			bind:value={newMsgContent}
			disabled={inAnswer}
		></textarea>
		<button
			type="button"
			class="btn btn-secondary absolute right-[20px] bottom-[20px]"
			disabled={newMsgContent.trim() == '' || inAnswer}
			onclick={() => sendMessage()}>发送</button
		>
	</div>
</div>
